import { Validator } from "./Validator";
import { TypeValidators } from "./validators";

export class Controller {
    private validators: Record<string,typeof Validator>;
    private validateActions: Record<string, Validation.IRegistedValidate<TypeValidators>>;
    private validateActionsWithGroup: Record<string, Record<string, Validation.IRegistedValidate<TypeValidators>>>;
    private validateActionsWithTag: Record<string, Record<string, Validation.IRegistedValidate<TypeValidators>>> = {};
    private validateActionsWithTagAndGroup: Record<string, Record<string, Record<string, Validation.IRegistedValidate<TypeValidators>>>> = {};
    constructor(option: Validation.IControllerOption) {
        this.validators = option.validators || {};
        this.validateActions = {};
        this.validateActionsWithGroup = {};
        this.validateActionsWithTag = {};
        this.validateActionsWithTagAndGroup = {};
    }
    /**
     * 注册验证规则
     * @param data 
     */
    public registe(data: Validation.IRegisteData<TypeValidators>): void {
        const registedValidateRules: Validation.IRegistedValidateRule<TypeValidators>[] = [];
        for(const rule of data.rules) {
            const ValidatorFactory: any = this.validators[rule.type];
            if(!ValidatorFactory) {
                throw new Error(`定义验证规则不存在。${rule.type}`);
            } else {
                const valdiatorOptions: Omit<Validation.IValidatorOptions, "validateInstance"> = {
                    type: rule.type,
                    validatedTag: data.tag,
                    validatedGroup: data.group,
                    validatedId: data.id,
                    name: rule.name,
                    message: rule.message,
                    defaultValue: data.defaultValue
                };
                registedValidateRules.push({
                    ...rule,
                    validateInstance: new ValidatorFactory(valdiatorOptions)
                });
            }
        }
        if(data.group && !data.tag) {
            if(!this.validateActionsWithGroup[data.group]) {
                this.validateActionsWithGroup[data.group] = {};
            }
            if(this.validateActionsWithGroup[data.group][data.id]) {
                throw new Error(`定义验证规则id重复，请检查设置：${data.group}.${data.id}`);
            }
            this.validateActionsWithGroup[data.group][data.id] = {
                ...data,
                rules: registedValidateRules
            };
        } else if(data.tag && !data.group) {
            if(!this.validateActionsWithTag[data.tag]) {
                this.validateActionsWithTag[data.tag] = {};
            }
            if(this.validateActionsWithTag[data.tag][data.id]) {
                throw new Error(`定义验证规则id重复，请检查设置：${data.group}.${data.id}`);
            }
            this.validateActionsWithTag[data.tag][data.id] = {
                ...data,
                rules: registedValidateRules
            };
        } else if(data.tag && data.group) {
            if(!this.validateActionsWithTagAndGroup[data.tag]) {
                this.validateActionsWithTagAndGroup[data.tag] = {};
            }
            if(!this.validateActionsWithTagAndGroup[data.tag][data.group]) {
                this.validateActionsWithTagAndGroup[data.tag][data.group] = {};
            }
            if(this.validateActionsWithTagAndGroup[data.tag][data.group][data.id]) {
                throw new Error(`定义验证规则id重复，请检查设置：${data.group}.${data.id}`);
            }
            this.validateActionsWithTagAndGroup[data.tag][data.group][data.id] = {
                ...data,
                rules: registedValidateRules
            };
        } else {
            if(this.validateActions[data.id]) {
                throw new Error(`定义验证规则id重复，请检查设置：${data.id}`);
            }
            this.validateActions[data.id] = {
                ...data,
                rules: registedValidateRules
            };
        }
    }
    /**
     * 注销验证规则
     * @param data 
     */
    public unRegiste(data: Omit<Validation.IRegisteData<TypeValidators>, "rules">): void {
        if(data.group) {
            if(this.validateActionsWithGroup[data.group]) {
                delete this.validateActionsWithGroup[data.group][data.id];
            }
        } else {
            delete this.validateActions[data.id];
        }
    }
    public validateById(id: string, option: Validation.IValidateOption): Validator<any> | undefined {
        const tag = option.tag, group = option.group;
        let validationAction = null;
        if(tag && group) {
            validationAction = this.validateActionsWithTagAndGroup[tag][group][id];
        } else if(tag && !group) {
            validationAction = this.validateActionsWithTag[tag][id];
        } else if(!tag && group) {
            validationAction = this.validateActionsWithGroup[group][id];
        } else {
            validationAction = this.validateActions[id];
        }
        return validationAction && this.validateRule(validationAction, option);
    }
    public validateWithGroupAndId(id: string, group: string, option: Validation.IValidateOption): Validator<any> | undefined {
        if(this.validateActionsWithGroup[group]) {
            const validate = this.validateActionsWithGroup[group][id];
            return validate && this.validateRule(validate, option);
        }
    }

    /**
     * 验证tag相同的校验规则
     * @param tag 
     * @param option 
     * @returns 
     */
    public validateByTag(tag: string, option: Validation.IValidateOption) {
        const validateActions = this.validateActionsWithTag[tag];
        const errorValidators: Validator<any>[] = [];
        if(validateActions) {
            const vActionKeys = Object.keys(validateActions);
            const valueData = option.value || {};
            for(const key of vActionKeys) {
                const validate = validateActions[key];
                const errorValidator = this.validateRule(validate, {
                    value: valueData[validate.id]
                });
                if(errorValidator) {
                    errorValidators.push(errorValidator);
                }
            }
        }
        return errorValidators;
    }
    /**
     * 触发tag和group相同的校验规则
     * @param tag 
     * @param group 
     * @param option 
     * @returns 
     */
    public validateByTagAndGroup(tag: string,group: string, option: Validation.IValidateOption) {
        const validateActions = this.validateActionsWithTagAndGroup[tag][group];
        const errorValidators: Validator<any>[] = [];
        if(validateActions) {
            const vActionKeys = Object.keys(validateActions);
            const valueData = option.value || {};
            for(const key of vActionKeys) {
                const validate = validateActions[key];
                const errorValidator = this.validateRule(validate, {
                    value: valueData[validate.id]
                });
                if(errorValidator) {
                    errorValidators.push(errorValidator);
                }
            }
        }
        return errorValidators;
    }
    /**
     * 当验证配置属性有变动，更新验证规则
     * @param id 
     * @param rules 
     * @param group 
     */
    public updateValidatedRules(id: string, rules: Validation.IValidateRule<any, any, any>[], tag?: string, group?: string): void {
        let validationAction = null;
        if(tag && group) {
            validationAction = this.validateActionsWithTagAndGroup[tag][group][id];
        } else if(tag && !group) {
            validationAction = this.validateActionsWithTag[tag][id];
        } else if(!tag && group) {
            validationAction = this.validateActionsWithGroup[group][id];
        } else {
            validationAction = this.validateActions[id];
        }
        if(validationAction) {
            for(let i=0;i<validationAction.rules.length;i++) {
                const oldRule = validationAction.rules[i];
                validationAction.rules[i] = {
                    ...oldRule,
                    ...rules[i]
                };
            }
        }
    }
    private validateRule(rule: Validation.IRegistedValidate<TypeValidators>, option: Validation.IValidateOption): Validator|undefined {
        for(const item of rule.rules) {
            if(!item.validateInstance.validate(option.value, option)) {
                item.validateInstance.error(item.message, item.name);
                return item.validateInstance;
            }
        }
    }
}
